package org.sean.framework.logging;

import org.slf4j.LoggerFactory;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.UnknownHostException;
import java.util.function.Supplier;


/**
 * <h3>Typical usage pattern:</h3>
 * <pre>
 *
 * public class Wombat {
 *
 *   <span style="color:green">private final Logger logger = Logger.newInstance(getClass());</span>
 *   Integer t;
 *   Integer oldT;
 *
 *   public void setTemperature(Integer temperature) {
 *     oldT = t;
 *     t = temperature;
 *     <span style="color:green">logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);</span>
 *     if(temperature.intValue() &gt; 50) {
 *       <span style="color:green">logger.info("Temperature has risen above 50 degrees.");</span>
 *     }
 *   }
 * }
 * </pre>
 *
 * @author xielei
 * @since 6.2.0
 */
public class Logger {
    public static final String KEY_TRACE = "X-B3-TraceId";
    public static final String KEY_SPAN = "X-B3-SpanId";

    private final org.slf4j.Logger log;

    protected Logger(Class<?> clazz) {
        log = LoggerFactory.getLogger(clazz);
    }

    public static Logger newInstance(Class<?> clazz) {
        return new Logger(clazz);
    }

    /**
     * Handy function to get a loggable stack trace from a Throwable
     *
     * @param tr An exception to log
     * @return 栈信息
     */
    public static String getStackTrace(Throwable tr) {
        if (tr == null) {
            return "";
        }

        // This is to reduce the amount of log spew that apps do in the non-error
        // condition of the network being unavailable.
        Throwable t = tr;
        while (t != null) {
            if (t instanceof UnknownHostException) {
                return " UnknownHostException ";
            }
            t = t.getCause();
        }

        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        tr.printStackTrace(pw);
        pw.flush();
        return sw.toString();
    }

    /**
     * @return 栈信息
     */
    public static String getStackTrace() {
        return getStackTrace(0);
    }

    /**
     * @param numbers 行数
     * @return 栈信息
     */
    public static String getStackTrace(int numbers) {
        StringBuilder sb = new StringBuilder();
        try {
            StackTraceElement[] stacks = Thread.currentThread().getStackTrace();
            sb.append("\nStackTrace:\n");
            StackTraceElement stack;
            int count = stacks.length;
            if (numbers > 0) {
                count = Math.min(numbers, stacks.length);
            }
            for (int i = 0; i < count; i++) {
                stack = stacks[i];
                sb.append("    at ").append(stack.getClassName()).append(".").append(stack.getMethodName())
                        .append("(").append(stack.getFileName()).append(stack.getLineNumber()).append(")\n");
            }
        } catch (Exception t) {
            // do nothing
        }
        return sb.toString();
    }

    /**
     * 打印日志
     *
     * @param message 消息内容
     * @param params  参数
     */
    public void debug(String message, Object... params) {
        if (log.isDebugEnabled()) {
            log.debug(message, params);
        }
    }

    /**
     * 打印日志
     *
     * @param supplier 消息内容
     */
    public void debug(Supplier<String> supplier) {
        if (log.isDebugEnabled()) {
            log.debug(supplier.get());
        }
    }

    /**
     * 打印日志
     *
     * @param supplier 消息内容
     */
    public void debugThrowable(Supplier<Throwable> supplier) {
        if (log.isDebugEnabled()) {
            log.debug("Error : ", supplier.get());
        }
    }

    /**
     * 打印日志
     *
     * @param message 消息内容
     * @param params  参数
     */
    public void info(String message, Object... params) {
        if (log.isInfoEnabled()) {
            log.info(message, params);
        }
    }

    /**
     * 打印日志
     *
     * @param supplier 消息内容
     */
    public void info(Supplier<String> supplier) {
        if (log.isInfoEnabled()) {
            log.info(supplier.get());
        }
    }

    /**
     * 打印日志
     *
     * @param message 消息内容
     * @param params  参数
     */
    public void warn(String message, Object... params) {
        if (log.isWarnEnabled()) {
            log.warn(message, params);
        }
    }

    /**
     * 打印日志
     *
     * @param message 消息内容
     * @param t       异常
     */
    public void warn(String message, Throwable t) {
        if (log.isWarnEnabled()) {
            log.warn(message, t);
        }
    }

    /**
     * 打印日志
     *
     * @param message 消息内容
     * @param params  参数
     */
    public void error(String message, Object... params) {
        if (log.isErrorEnabled()) {
            log.error(message, params);
        }
    }

    /**
     * 打印日志
     *
     * @param message 消息内容
     * @param e       异常
     */
    public void error(String message, Throwable e) {
        if (log.isErrorEnabled()) {
            log.error(message, e);
        }
    }

    /**
     * 打印异常
     *
     * @param e 异常
     */
    public void error(Throwable e) {
        if (log.isErrorEnabled()) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 打印异常信息
     *
     * @param e 异常
     */
    public void printStackTrace(Throwable e) {
        log.info(e.getMessage(), e);
    }

    /**
     * 打印异常信息
     *
     * @param e 异常
     */
    public void printWarnStackTrace(Throwable e) {
        log.warn(e.getMessage(), e);
    }

    /**
     * 打印异常信息
     *
     * @param e 异常
     */
    public void printErrorStackTrace(Throwable e) {
        log.error(e.getMessage(), e);
    }

}
